//
//  DemoZip.m
//  ocdemo
//
//  Created by JIENING ZHANG on 2019/8/9.
//  Copyright © 2019 JIENING ZHANG. All rights reserved.
//

#import "DemoZip.h"
#import "zlib.h"

@implementation DemoZip

+ (NSData*)gzipData:(NSData *)data4Zip {
    // https://www.jianshu.com/p/dc76bcfef553
    //Xcode 提供的 zlib 可以实现 gzip、defalte 这两种编码格式。
    
    if([data4Zip length]==0) {
        return nil;
    }
    
    
    
    // 1 of 3, 初始化 z_stream
    //z_stream 的初始化过程如下，其中 zalloc、zfree、opaque 设置为 NULL 这样之后的 deflateInit2() 方法会把这些指针更新为默认的内存管理函数。
    z_stream zStream;
    bzero(&zStream, sizeof(zStream));
    
    zStream.zalloc = Z_NULL;
    zStream.zfree = Z_NULL;
    zStream.opaque = Z_NULL;
    // 剩余的需要压缩字指针
    zStream.next_in = (Bytef *) data4Zip.bytes;
    // 剩余的需要压缩字节数
    zStream.avail_in = (uInt) data4Zip.length;
    // 目前已经输出的字节数
    zStream.total_out = 0;
    
    
    
    // 2 of 3, 初始化 deflate
    
    // deflateInit2 参数：
    //  z_streamp strm:     z_stream 指针；
    //  int level:          压缩等级，必需为 Z_DEFAULT_COMPRESSION 或者 0 ~ 9 的整数，1为最快，9为最大限度压缩，0为不压缩，数字越大越耗时；
    //  int method:         压缩算法，只支持 Z_DEFLATED；
    //  int windowBits:     历史缓冲区最大尺寸，值为 2^windowBits,
    //                          windowBits 的值为 8~15 时，deflate() 方法生成 zlib 格式的数据，
    //                          当 windowBits 为 31 时 deflate() 方法生成 gzip 格式。
    //                          当取值为 -15 ~ -8 时，deflate() 生成纯 deflate 算法压缩数据（不包含 zlib 和 gzip 格式头和尾）
    // int strategy:        用于调整压缩算法，一般使用 Z_DEFAULT_STRATEGY
    
    OSStatus status = deflateInit2(&zStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
    
    if (status != Z_OK) {
        return nil;
    }
    
    
    
    // 3 of 3, 使用 deflate，按kZlibCompressChunkSize 做数据压缩
    // 循环调用 deflate () 方法不断压缩数据并填充到 NSMutableData 中，直到 deflate () 返回结果不为 Z_BUF_ERROR 或者 Z_OK 为止。
    static NSInteger kZlibCompressChunkSize = 2048;
    NSMutableData *compressedData = [NSMutableData dataWithLength:kZlibCompressChunkSize];
    do {
        if ((status == Z_BUF_ERROR) || (zStream.total_out == compressedData.length)) {
            [compressedData increaseLengthBy:kZlibCompressChunkSize];
        }
        
        zStream.next_out = (Bytef *)compressedData.bytes + zStream.total_out;
        zStream.avail_out = (uInt)(compressedData.length - zStream.total_out);
        status = deflate(&zStream, Z_FINISH);
    } while ((status == Z_BUF_ERROR) || (status == Z_OK));
    
    status = deflateEnd(&zStream);
    
    if ((status != Z_OK) && (status != Z_STREAM_END)) {
        return nil;
    }
    
    compressedData.length = zStream.total_out;
    
    return [NSData dataWithData: compressedData];;
}

+ (NSData*)ungzipData:(NSData *)data4Unzip {
    if([data4Unzip length]==0) {
        return nil;
    }
    

    
    // 1 of 3, 初始化 z_stream
    z_stream strm;
    strm.next_in = (Bytef *)[data4Unzip bytes];
    strm.avail_in = (uInt)[data4Unzip length];
    strm.total_out = 0;
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;

    
    
    // 2 of 3, 初始化 inflate
    if (inflateInit2(&strm, (15+32)) != Z_OK) {
        return nil;
    }
    
    
    
    // 3 of 3,  循环调用 inflate () 方法不断解压缩数据并填充到 NSMutableData 中
    NSUInteger fullLen = [data4Unzip length];
    NSUInteger halfLen = [data4Unzip length] / 2;
    NSMutableData *decompressed = [NSMutableData dataWithLength: fullLen + halfLen];
    BOOL done = NO;
    int status;
    
    while (!done) {
        // Make sure we have enough room and reset the lengths.
        if (strm.total_out >= [decompressed length]) {
            [decompressed increaseLengthBy: halfLen];
        }
        
        strm.next_out = [decompressed mutableBytes] + strm.total_out;
        strm.avail_out = (uInt)([decompressed length] - strm.total_out);
        
        // Inflate another chunk.
        status = inflate(&strm, Z_SYNC_FLUSH);

        if (status == Z_STREAM_END) {
            done = YES;
        } else if (status != Z_OK) {
            break;
        }
    }
    
    if (inflateEnd (&strm) != Z_OK){
        return nil;
    }
    
    if (done) {
        [decompressed setLength: strm.total_out];
        return [NSData dataWithData: decompressed];
    } else {
        return nil;
    }
}

+ (void)printBytes:(NSData *)data {
    if (data && data.length > 0) {
        
        Byte *datas = (Byte*)[data bytes];
        NSMutableString *output = [NSMutableString stringWithCapacity:data.length * 2];
        for(int i = 0; i < data.length; i++){
            [output appendFormat:@"%02x ", datas[i]];
        }
        NSLog(@"printBytes %@: %@", data, output);
    }
}

+ (void)demoLogic {
    
    NSString *oStr = @"七律·长征 \
    毛泽东 \
    1935年10月 \
    红军不怕远征难，万水千山只等闲 \
    五岭逶迤腾细浪，乌蒙磅礴走泥丸。\
    金沙水拍云崖暖，大渡桥横铁索寒。\
    更喜岷山千里雪，三军过后尽开颜。";
    
    NSLog(@"原字符串 : %@", oStr);
    
    NSData *oData = [oStr dataUsingEncoding:NSUTF8StringEncoding];
    NSLog(@"原字符串nsdata");
    [DemoZip printBytes:oData];
    
    NSData *gzipData = [DemoZip gzipData:oData];
    NSLog(@"gzip nsdata");
    [DemoZip printBytes:gzipData];
    
    NSData *ungzipData = [DemoZip ungzipData:gzipData];
     NSLog(@"ungzip nsdata");
    [DemoZip printBytes:ungzipData];
    
    NSString *strFromUngzipData = [[NSString alloc]initWithData:ungzipData encoding:NSUTF8StringEncoding];
    NSLog(@"解压缩data还原字符串 : %@", strFromUngzipData);
    
    
    
    
    
    oStr = @"Never in the field of human conflict was so much owed by so many to so few";
    NSLog(@"原字符串 : %@", oStr);
    
    oData = [oStr dataUsingEncoding:NSUTF8StringEncoding];
    NSLog(@"原字符串nsdata");
    [DemoZip printBytes:oData];
    
    gzipData = [DemoZip gzipData:oData];
    NSLog(@"gzip nsdata");
    [DemoZip printBytes:gzipData];
    
    ungzipData = [DemoZip ungzipData:gzipData];
    NSLog(@"ungzip nsdata");
    [DemoZip printBytes:ungzipData];
    
    strFromUngzipData = [[NSString alloc]initWithData:ungzipData encoding:NSUTF8StringEncoding];
    NSLog(@"解压缩data还原字符串 : %@", strFromUngzipData);
    
}

@end
